﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.ComponentModel;
using xn;
using System.Threading;
using System.Drawing.Imaging;
using System.Drawing;
using System.Windows.Media.Imaging;
using System.IO;

namespace KinectCore.hwKinectComponents
{
    /// <summary>
    /// Interaction logic for KinectTextBlock.xaml
    /// </summary>
    public class HWKinectSilhouettePainter : UserControl
    {
        private Context _context;
        private DepthGenerator _depth;
        private UserGenerator _userGenerator;
        private bool _shouldRun;
        private Thread _readerThread;
        private Bitmap _bitmap;
        private System.Windows.Controls.Image _image;

        private readonly System.Windows.Media.Color[] colors = { Colors.Red, Colors.Blue, Colors.ForestGreen, Colors.Yellow, Colors.Orange, Colors.Purple, Colors.White };
        private readonly System.Windows.Media.Color[] anticolors = { Colors.Green, Colors.Orange, Colors.Red, Colors.Purple, Colors.Blue, Colors.Yellow, Colors.Black };
        private readonly int ncolors = 6;

        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);

            Border border = new Border();
            border.VerticalAlignment = VerticalAlignment.Stretch;
            border.HorizontalAlignment = HorizontalAlignment.Stretch;

            Grid grid = new Grid();

            ContentPresenter content = new ContentPresenter();
            content.Content = Content;

            grid.Children.Add(content);

            _image = new System.Windows.Controls.Image();
            grid.Children.Add(_image);

            CreateSilhouetteBitmap();

            border.Child = grid;

            Content = border;
        }

        private void CreateSilhouetteBitmap()
        {
            // get our context and depth map generator
            _context = new Context("KinectConfig.xml");
            _depth = _context.FindExistingNode(NodeType.Depth) as DepthGenerator;
            if (_depth == null)
            {
                throw new Exception("Viewer must have a depth node!");
            }

            // get a user generator
            _userGenerator = new UserGenerator(_context);
            //_userGenerator.NewUser += new UserGenerator.NewUserHandler(userGenerator_NewUser);
            //_userGenerator.LostUser += new UserGenerator.LostUserHandler(userGenerator_LostUser);
            _userGenerator.StartGenerating();

            MapOutputMode mapMode = _depth.GetMapOutputMode();

            // init the bitmap
            _bitmap = new Bitmap((int)mapMode.nXRes, (int)mapMode.nYRes);

            // init the thread
            _shouldRun = true;
            _readerThread = new Thread(ReaderThread);

            //CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);

            _readerThread.Start();
        }

        /// <summary>
        /// Called on every render
        /// </summary>
        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            //TODO: This seems to be bombing the UI Thread meaning that other UI elements aren't drawn.
            //Update the image
            lock (_bitmap)
            {
                _image.Source = Extensions.loadBitmap(_bitmap);
            }
        }

        private unsafe void ReaderThread()
        {
            DepthMetaData depthMD = new DepthMetaData();

            while (_shouldRun)
            {
                lock (this)
                {
                    lock (_bitmap)
                    {
                        try
                        {
                            _context.WaitOneUpdateAll(_depth);
                        }
                        catch (Exception)
                        {}

                        _depth.GetMetaData(depthMD);

                        //CalcHist(depthMD);

                        Rectangle rect = new System.Drawing.Rectangle(0, 0, _bitmap.Width, _bitmap.Height);
                        BitmapData data = _bitmap.LockBits(rect, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);


                        // draw pixels
                        ushort* pDepth = (ushort*)_depth.GetDepthMapPtr().ToPointer();
                        ushort* pLabels = (ushort*)_userGenerator.GetUserPixels(0).SceneMapPtr.ToPointer();

                        // set pixels
                        for (int y = 0; y < depthMD.YRes; ++y)
                        {
                            byte* pDest = (byte*)data.Scan0.ToPointer() + y * data.Stride;
                            for (int x = 0; x < depthMD.XRes; ++x, ++pDepth, ++pLabels, pDest += 3)
                            {
                                pDest[0] = pDest[1] = pDest[2] = 0;

                                ushort label = *pLabels;
                                if (*pLabels != 0)
                                {
                                    var labelColor = Colors.White;
                                    if (label != 0)
                                    {
                                        labelColor = colors[label % ncolors];
                                    }

                                    pDest[0] = labelColor.B;
                                    pDest[1] = labelColor.G;
                                    pDest[2] = labelColor.R;
                                }
                            }
                        }
                        _bitmap.UnlockBits(data);
                    }
                    _image.Source = Extensions.loadBitmap(_bitmap);
                }
                Thread.Sleep(10);
            }
        }

        public void Start()
        {
            _readerThread.Start();
        }

        public void Stop()
        {
            _shouldRun = false;
            if (_readerThread != null && _readerThread.ThreadState != ThreadState.Unstarted)
                _readerThread.Join();
        }

        /*
        /// <summary>
        /// Cross-thread access means we have to update the UI on the UI thread.
        /// </summary>
        private void UpdateUi(Action action)
        {
            Dispatcher.Invoke(DispatcherPriority.Background, action);
        }*/
    }
}
